﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using H2.Streams;
using H2.DataTypes;

namespace Mystery
{
    public class Placement
    {
        public const int BipedsOffset = 96;
        public const int WeaponsOffset = 144;
        public const int VehiclesOffset = 112;
        public const int EquipmentOffset = 128;

        public const int SceneryOffset = 80;
        public const int MachinesOffset = 168;
        public const int ControlsOffset = 184;
        public const int LightFixturesOffset = 200;
        public const int SoundSceneryOffset = 216;
        public const int LightVolumesOffset = 232;
        public const int CratesOffset = 808;

        public const int BipedsPlacementChunkSize = 84;
        public const int WeaponsPlacementChunkSize = 84;
        public const int VehiclesPlacementChunkSize = 84;
        public const int EquipmentPlacementChunkSize = 56;

        public const int SceneryPlacementChunkSize = 92;
        public const int MachinesPlacementChunkSize = 72;
        public const int ControlsPlacementChunkSize = 68;
        public const int LightFixturesPlacementChunkSize = 80;
        public const int SoundSceneryPlacementChunkSize = 80;
        public const int LightVolumesPlacementChunkSize = 108;
        public const int CratesPlacementChunkSize = 76;

        public int TypeIndex;
        public int NameIndex;

        public float PositionX;
        public float PositionY;
        public float PositionZ;

        public float RotationYaw;
        public float RotationPitch;
        public float RotationRoll;

        public short BSPIndex;

        public StringID VariantName;

        public static Placement[] ReadBipeds(MapStream map, int scnrOffset, int sbspIndex)
        { return Read(map, map.ReadReflexiveAt(scnrOffset + Placement.BipedsOffset), sbspIndex, BipedsPlacementChunkSize, true); }

        public static Placement[] ReadWeapons(MapStream map, int scnrOffset, int sbspIndex)
        { return Read(map, map.ReadReflexiveAt(scnrOffset + Placement.WeaponsOffset), sbspIndex, WeaponsPlacementChunkSize, true); }

        public static Placement[] ReadVehicles(MapStream map, int scnrOffset, int sbspIndex)
        { return Read(map, map.ReadReflexiveAt(scnrOffset + Placement.VehiclesOffset), sbspIndex, VehiclesPlacementChunkSize, true); }

        public static Placement[] ReadEquipment(MapStream map, int scnrOffset, int sbspIndex)
        { return Read(map, map.ReadReflexiveAt(scnrOffset + Placement.EquipmentOffset), sbspIndex, EquipmentPlacementChunkSize, false); }

        private static Placement[] Read(MapStream map, int chunkCount, int sbspIndex, int chunkSize, bool readVariant)
        {
            int startOffset = (int)map.Position;
            List<Placement> placements = new List<Placement>();
            for (int i = 0; i < chunkCount; i++)
            {
                int chunkOffset = startOffset + i * chunkSize;
                map.Position = chunkOffset;
                Placement placement = new Placement();
                placement.TypeIndex = map.ReadInt16();
                placement.NameIndex = map.ReadInt16();
                placement.PositionX = map.ReadFloatAt(chunkOffset + 8, false);
                placement.PositionY = map.ReadFloat();
                placement.PositionZ = map.ReadFloat();
                placement.RotationYaw = map.ReadFloat();
                placement.RotationPitch = map.ReadFloat();
                placement.RotationRoll = map.ReadFloat();
                placement.BSPIndex = map.ReadInt16At(chunkOffset + 44, false);

                if (readVariant)
                    placement.VariantName = map.ReadStringIDAt(chunkOffset + 58, false);
                else
                    placement.VariantName = new StringID();

                if (sbspIndex == -1 || placement.BSPIndex == sbspIndex)
                    placements.Add(placement);
            }
            return placements.ToArray();
        }

        public static void Update(MapStream map, int scnrOffset, int selectedSBSPIndex, int aiDifficulty, int placementOffset, int placementChunkSize)
        {
            int chunkCount = map.ReadReflexiveAt(scnrOffset + placementOffset);
            int startOffset = (int)map.Position;

            for (int i = 0; i < chunkCount; ++i)
            {
                int chunkStartOffset = startOffset + (i * placementChunkSize);

                int flags = map.ReadInt32At(chunkStartOffset + 4, false);
                switch (aiDifficulty)
                {
                    case 0://Easy
                        break;
                    case 1://Normal
                        if ((flags & 2) > 0) flags &= ~2;//If Not On Easy, Disable Not On Easy
                        if ((flags & 4) > 0) flags |= (2 | 64);//If Not On Normal, Enable Not On Easy
                        break;
                    case 2://Hard
                        if ((flags & 2) > 0) flags &= ~2;//If Not On Easy, Disable Not On Easy
                        if ((flags & 8) > 0) flags |= (2 | 64);//If Not On Hard, Enable Not On Easy;
                        break;
                    default://Legendary
                        if ((flags & 2) > 0) flags &= ~2;//If Not On Easy, Disable Not On Easy
                        break;
                }
                map.WriteAt(chunkStartOffset + 4, flags, false);

                if (selectedSBSPIndex >= 0)
                {
                    ushort originBSPFlags = map.ReadUInt16At(chunkStartOffset + 38, false);
                    short originBSP = map.ReadInt16At(chunkStartOffset + 44, false);

                    if ((originBSPFlags & (1 << selectedSBSPIndex)) > 0)
                        map.WriteAt(chunkStartOffset + 38, (ushort)(originBSPFlags | 1), false);

                    if ((originBSPFlags & 1) > 0)
                        map.WriteAt(chunkStartOffset + 38, (ushort)(originBSPFlags | (1 << selectedSBSPIndex)), false);

                    if (originBSP == selectedSBSPIndex)
                        map.WriteAt(chunkStartOffset + 44, (short)0, false);
                    if (originBSP == 0)
                        map.WriteAt(chunkStartOffset + 44, (short)selectedSBSPIndex, false);
                }
            }
        }

        public static void NullPlacements(MapStream map, int scnrOffset, int placementOffset, int placementChunkSize, bool keepScripts)
        {
            int chunkCount = map.ReadReflexiveAt(scnrOffset + placementOffset);
            int startOffset = (int)map.Position;

            for (int i = 0; i < chunkCount; ++i)
            {
                if (keepScripts)
                {
                    map.Position = startOffset + (i * placementChunkSize) + 2;
                    short typeIndex = map.ReadInt16();
                    if (typeIndex >= 0) continue;
                }

                map.Position = startOffset + (i * placementChunkSize);
                map.Write((short)-1);
            }
                    
        }
    }
}
